package cn.echo.ecc.service.impl;

import cn.echo.ecc.config.HighlightResultMapper;
import cn.echo.ecc.doc.CourseDoc;
import cn.echo.ecc.dto.CourseEsDto;
import cn.echo.ecc.repository.CourseESRepository;
import cn.echo.ecc.result.EsAggrPageList;
import cn.echo.ecc.result.PageList;
import cn.echo.ecc.service.ICourseESSearchService;
import cn.echo.ecc.vo.AggreTermsBucketVo;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CourseESSearchServiceImpl implements ICourseESSearchService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private CourseESRepository courseESRepository;

    @Override
    public void saveCourse(CourseDoc courseDoc) {
        courseESRepository.save(courseDoc);
    }

    @Override
    public void deleteCourse(Long id) {
        courseESRepository.deleteById(id);
    }

    @Override
    public PageList<CourseDoc> search(CourseEsDto courseEsDto) {
        String keyword = courseEsDto.getKeyword();
        String gradeName = courseEsDto.getGradeName();
        Long courseTypeId = courseEsDto.getCourseTypeId();
        Double priceMax = courseEsDto.getPriceMax();
        Double priceMin = courseEsDto.getPriceMin();
        String sortField = courseEsDto.getSortField();
        String sortType = courseEsDto.getSortType();
        String chargeName = courseEsDto.getChargeName();
        // 1.创建原生的条件查询构造器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 2.创建高亮映射对象
        HighlightResultMapper highlightResultMapper = new HighlightResultMapper();
        // 3.通过QueryBuilders工具类获取boolQuery对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 4.判断keyword不为空，关键字模糊查询
        if (!StringUtils.isEmpty(keyword)) {
            boolQuery.must(QueryBuilders.matchQuery("name",keyword));
        }
        // 5.判断等级不为空，过滤精准查询
        if (!StringUtils.isEmpty(gradeName)) {
            boolQuery.filter(QueryBuilders.termQuery("gradeName",gradeName));
        }
        // 6.判断课程类型不为空，过滤精准查询
        if (courseTypeId != null) {
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",courseTypeId));
        }
        // 7.判断是否免费不为空，过滤精准查询
        if (!StringUtils.isEmpty(chargeName)) {
            boolQuery.filter(QueryBuilders.termQuery("charge",chargeName));
        }
        // 8.判断最大价格和最小价格不为空，过滤区间范围查询
        if (priceMax != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(priceMax));
        }
        if (priceMin != null) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(priceMin));
        }
        // 9.设置多条件查询
        nativeSearchQueryBuilder.withQuery(boolQuery);
        // 10.判断排序字段和排序类型不为空，设置排序查询
        if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortType)) {
            SortOrder sortOrder = "asc".equalsIgnoreCase(sortType) ? SortOrder.ASC : SortOrder.DESC;
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortOrder));
        }
        // 11.设置分页查询
        nativeSearchQueryBuilder.withPageable(PageRequest.of(courseEsDto.getPage() - 1,courseEsDto.getRows()));
        // 12.设置高亮
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>"));
        // 13.设置聚合
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("gradeNameTermsAgg").field("gradeName"));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("chargeNameTermsAgg").field("charge"));
        // 14.创建多条件查询对象
        NativeSearchQuery build = nativeSearchQueryBuilder.build();
        // 15.使用elasticsearchRestTemplate对象进行分页高级查询
        AggregatedPage<CourseDoc> courseDocs = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);
        // 16.封装聚合后，需要提供给前端展示的对象AggreTermsBucketVo
        Aggregations aggregations = courseDocs.getAggregations();
        Map<String, List<AggreTermsBucketVo>> map = new HashMap<>();
        aggregations.asMap().entrySet().forEach(e -> {
            List<AggreTermsBucketVo> aggreTermsBucketVos = new ArrayList<>();
            String key = e.getKey();
            ParsedStringTerms parsedStringTerms = (ParsedStringTerms)e.getValue();
            parsedStringTerms.getBuckets().forEach(p -> {
                AggreTermsBucketVo aggreTermsBucketVo = new AggreTermsBucketVo();
                String keyAsString = p.getKeyAsString();
                long docCount = p.getDocCount();
                aggreTermsBucketVo.setKey(keyAsString);
                aggreTermsBucketVo.setDocCount(docCount);
                aggreTermsBucketVos.add(aggreTermsBucketVo);
            });
            map.put(key,aggreTermsBucketVos);
        });
        // 17.返回高亮、聚合、多条件查询的数据
        return new EsAggrPageList<>(courseDocs.getTotalElements(),courseDocs.getContent(),map);
    }
}
